package com.shen.shensdcard

import android.app.RecoverableSecurityException
import android.content.ContentUris
import android.content.ContentValues
import android.content.Context
import android.graphics.Bitmap
import android.media.ExifInterface
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.os.StatFs
import android.os.storage.StorageManager
import android.provider.MediaStore
import android.util.Log
import android.util.Size
import androidx.annotation.RequiresApi
import androidx.core.app.ActivityCompat.startIntentSenderForResult
import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import androidx.core.os.BuildCompat
import java.io.*
import java.util.*
import java.util.concurrent.TimeUnit

/**
 * author:  shen
 * date:    2021/6/7
 *
 *
 * Android 提供了两类物理存储位置：「内部存储空间」和「外部存储空间」。
 *  在大多数设备上，「内部存储空间」小于「外部存储空间」。
 *
 * 一、「内部存储空间」目录：/data/data/包名/   --  不需要任何「系统权限」
 *      1、「持久性文件·根目录」: /data/data/包名/files/  -- 使用方法: context.filesDir()
 *      2、「缓存性文件·根目录」: /data/data/包名/cache/  -- 使用方法: context.cacheDir()
 *      android也提供了一些简便的api创建删除文件:
 *          context.openFileOutput(filename, Context.MODE_PRIVATE)
 *          context.openFileInput(filename)
 *          context.fileList()
 *          context.getDir(dirName, Context.MODE_PRIVATE)
 *          context.deleteFile(fileName)
 *      注意：卸载app后，系统会自动移除这些目录释放空间！！
 *
 *
 * 二、「外部存储空间」目录：/storage/emulated/0/Android/data/包名   -- /storage 或 /mnt 目录
 *
 *      Android 10 「android Q / api 29」 -- 「分区存储特性」：对Android系统的「外部存储空间」重新设计
 *          「外部存储空间」被分为：「应用私有目录」以及「共享目录」两个部分：
 *              1、「应用私有目录」 : /storage/emulated/0/Android/data/包名    -- 不需要任何「系统权限」
 *              2、「共享目录」    : 存储「其他应用·可访问文件」
 *                  [Environment.DIRECTORY_MUSIC]
 *                  [Environment.DIRECTORY_PODCASTS]
 *                  [Environment.DIRECTORY_ALARMS]
 *                  [Environment.DIRECTORY_RINGTONES]
 *                  [Environment.DIRECTORY_NOTIFICATIONS]
 *                  [Environment.DIRECTORY_PICTURES]
 *                  [Environment.DIRECTORY_DOWNLOADS]
 *                  [Environment.DIRECTORY_DCIM]
 *                  [Environment.DIRECTORY_DOCUMENTS]
 *                  [Environment.DIRECTORY_AUDIOBOOKS]
 *
 *              「应用私有目录」
 *                  (1)、「持久性文件·根目录」: /storage/emulated/0/Android/data/包名/files/共享目录
 *                      -- 使用方法: ContextCompat.getExternalFilesDirs(@NonNull Context context, @Nullable String type)
 *                      如：[Environment.DIRECTORY_PICTURES]图片，此时返回/storage/emulated/0/Android/data/包名/files/Pictures
 *                  (2)、「缓存性文件·根目录」: /storage/emulated/0/Android/data/包名/cache  -- 使用方法: ContextCompat.getExternalCacheDirs(context)
 *
 *              「共享目录」
 *                  (1)、媒体内容:         可以使用 [MediaStore API] 访问此内容
 *                  (2)、文档和其他文件:    系统有一个特殊目录，用于包含其他文件类型，例如 PDF 文档和采用 EPUB 格式的图书。
 *                                          应用可以使用[Storage Access Framework]访问这些文件。
 *                  对于共享文件:
 *                      「Android 10 以前」可以通过「data column」获得「路径」，再使用「File API」来操作「文件」； 「现在」都会「返回失败」。
 *                      开启了「分区存储特性」后：
 *                          「应用」只能通过「系统提供的api」来「向系统请求」得到对应「文件的Uri」，
 *                          并通过[Uri]生成[FileDescriptor]和[InputStream]等方式进行文件读写
 *                          （简而言之，对于共享文件的增删查改，主要问题在于Uri的获取）
 *
 *                  注：「Android 11」又允许通过「路径」来访问，系统会「自动重定向」为「Uri」。
 *                      val resolver = applicationContext.contentResolver
 *
 *                      resolver.openFileDescriptor(content-uri, "r")?.use { pfd ->		// 读
 *                          val inputStream =  FileInputStream(pfd.fileDescriptor)
 *                      }
 *                      resolver.openInputStream(content-uri).use { stream ->
 *                      }
 *
 *                      resolver.openFileDescriptor(content-uri, "w")?.use { pfd ->		// 写
 *                          val outputStream =  FileOutputStream(pfd.fileDescriptor)
 *                      }
 *                      resolver.openOutputStream(content-uri).use { stream ->
 *                      }
 *
 *                      BitmapFactory.decodeFileDescriptor(pfd.fileDescriptor)			// 图片bitmap
 *
 *                  [MediaStore]
 *                      (1)、[MediaStore]概述
 *                          Android系统会自动扫描「外部存储空间」，将「媒体文件」按「类型」添加到「系统预定义的Images、Videos、Audio files、Downloaded files集合」中。
 *                          「Android 10」通过
 *                              [MediaStore.Images]、
 *                              [MediaStore.Video]、
 *                              [MediaStore.Audio]、
 *                              [MediaStore.Downloads]
 *                              访问相对应「共享目录」文件资源。
 *
 *                          媒体类型    Uri											默认创建目录      允许创建目录
 *                          Image		content://media/external/images/media		Pictures		DCIM,Pictures
 *                          Audio		content://media/external/audio/media		Music			Alarms,Music,Notifications,Podcasts,Ringtones
 *                          Video		content://media/external/video/media		Movies			DCIM,Movies
 *                          Download	content://media/external/downloads			Download		Download
 *
 *                          注意：[MediaStore.Downloads.EXTERNAL_CONTENT_URI]是[Android 10]·[新增API]，用于[创建、访问]非媒体文件
 *
 *                      (2)、[MediaStore]的变化
 *                          1)、MediaStore API 在「共享目录」指定目录下「创建文件·访问」应用「自己·创建的文件」，「不需要」申请「存储权限」
 *                          2)、MediaStore API 访问「其他应用」在「共享目录」创建的「媒体文件(图片、音频、视频)」，「需要」申请「存储权限」，
 *                              「未」申请「存储权限」，通过「ContentResolver」查询不到「文件Uri」，即使通过其他方式获取到文件Uri，读取或创建文件会「抛出异常」；
 *                          3)、MediaStore API 「不能够」访问「其他应用」创建的「非媒体文件(pdf、office、doc、txt等)」，
 *                              「Android 10」里唯一一种访问其他应用创建的非媒体文件的途径是使用「存储访问框架 (Storage Access Framework)」 提供的「文档选择器」。
 *
 *                      (3)、通过「api」创建的「文件」存放到哪里？如何自定义位置？
 *                          当通过[MediaStore API]创建文件时，文件会默认保存到对应的类型目录，
 *                          比如「图片」存到「Pictures/目录」下，可以「往上」查看 --「默认目录」及「允许目录」；
 *                          可以使用[MediaStore.xxx.Media.RELATIVE_PATH]自己指定「要存放·目录或者子目录」，
 *                          如：contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/自定义子目录")，
 *                              文件就会放在[Pictures/自定义子目录/]中；
 *                              或者使用
 *                              contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM)，
 *                              将文件放到[DCIM/]中
 *                          注意：每一种类型都有对应的可允许创建的目录，否则会返回失败。具体可创建目录可以往上查看表格
 *
 *                      (4)、Storage Access Framework
 *                          「SAF框架」支持「用户与系统选择器」互动，从而选择「文档提供器」以及「供您的应用创建、打开或修改」的「特定文档和其他文件」。
 *                          由于用户参与了文件的选择，因此该机制无需任何系统权限。
 *                          应用通过调用
 *                              [ACTION_CREATE_DOCUMENT],
 *                              [ACTION_OPEN_DOCUMENT],
 *                              [ACTION_OPEN_DOCUMENT_TREE]
 *                          Intent获取[Document provider]提供的文件，并在[onActivityResult 接口]接收[返回的被选择文件的Uri]。
 *                          另外，在配置 Intent 时，应指定「文件的名称」和「MIME类型」，
 *                          并且还可以根据需要使用 [EXTRA_INITIAL_URI intent extra] 指定「文件选择器」在「首次加载时」应显示的「文件或目录的 URI」。
 *
 *                          ps:获取持久权限
 *                              对于通过「SAF框架」获得的「uri权限」，可以通过申请持久权限，不用「每次重启手机」都要「重新请求」。
 *                              contentResolver.takePersistableUriPermission(documentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
 *           需要注意的不同点是：
 *              1、开启了「分区存储特性」后，应用只能访问「自己·应用私有目录」，即使获得了存储权限，也「无法访问」「其他应用·应用私有目录」
 *              2、「外部存储空间」与「内部存储空间」的区别是：「外部存储空间」可能「被移除」也可能有多个，
 *                  所以「返回」的是「一个数组」，对于「返回数组中·第一个元素」被视为「主外部存储卷」。除非该卷已满或不可用，否则请使用该卷。
 *
 *          注意：卸载app后，系统会自动移除「应用私有目录」释放空间！！
 *
 *
 *
 * 三、图片位置信息
 *      一些「照片」在其「Exif元数据」中包含「位置信息」，以便用户查看「照片的拍摄地点」。
 *      但是，由于此位置信息属于「敏感信息」，如果应用使用了「分区存储」，默认情况下 「Android 10」会对应用「隐藏此信息」。
 *      如果您的应用需要访问照片的位置信息，请完成以下步骤：
 *      在[应用的清单]中请求[ACCESS_MEDIA_LOCATION]权限。
 *      通过调用 setRequireOriginal()，从[MediaStore 对象]获取「照片的确切字节」，并传入「照片的 URI」，如以下代码段所示：
 *          val contentUri: Uri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, cursor.getLong(BaseColumns._ID))
 *          val fileOpenMode = "r"
 *          val parcelFd = resolver.openFileDescriptor(contentUri, fileOpenMode)
 *          val fd = parcelFd?.detachFd()
 *          // Pass the integer value "fd" into your native code. Remember to call
 *          // close(2) on the file descriptor when you're done using it.
 *
 * 四、更新
 *      1、「Android 10」的分区存储「bug」
 *         「Android 10」通过媒体「MediaStore API」删除（delete）一个媒体文件，只是简单「移除」了MediaStore数据库的「索引」，
 *         并不会真正删除物理存储上的实体文件，而且只要手机重启，则索引又被加上去了。
 *         「Android 11」就可以正常删除了。
 *
 *      2、「Android 11」的存储变更
 *          (1)、允许继续使用原始文件路径
 *              可以再次使用文件路径，系统自动重定向为Uri
 *
 *          (2)、增加批量操作
 *              在「Android 10」中，应用在对「MediaStore」的「每一个文件」请求编辑或删除时都必须「一个个地得到用户的确认」。
 *              在「Android 11」中，应用可以「一次请求」修改或者删除「多个媒体文件」。
 *
 *              主要通过以下新增的批量操作api：
 *                  方法													    说明
 *                  MediaStore.createDeleteRequest (resolver, uris)			批量删除（不放入回收站）
 *                  MediaStore.createFavoriteRequest(resolver, uris)		批量收藏
 *                  MediaStore.createTrashRequest (resolver, uris)			批量移入回收站
 *                  MediaStore.createWriteRequest(resolver, uris)			批量获得写入权限
 *
 *              操作：
 *                  val uris = ...
 *                  val pi = MediaStore.createWriteRequest(contentResolver, uris)
 *                  startIntentSenderForResult(pi.intentSender, REQUEST_CODE, null, 0, 0, 0)
 *                  //相应
 *                  override fun onActivityResult(xxx) {
 *                      when (requestCode) {
 *                          REQUEST_CODE -> if (resultCode == Activity.RESULT_OK) {
 *                              //获得权限，继续操作
 *                          } else {
 *                              // 用户拒绝了权限授予
 *                          }
 *                      }
 *                  }
 *
 *
 * [ContentUris]是content URI的一个辅助类。
 *  ContentUris有两个方法很有用，具体如下所示：
 *      public static Uri withAppendedId（Uri contentUri, long id），
 *          这个方法负责把id和contentUri连接成一个新的Uri。比如在我们这个例子当中是这么使用 的：
 *          ContentUris.withAppendedId（Diary.DiaryColumns.CONTENT_URI, rowId）。
 *          如果rowId为100的话，那么现在的这个Uri的内容就是：content://com.ex09_2_contentprovider.diarycontentprovider/diaries/100。
 *      public static long parseId（Uri contentUri），
 *          这个方法负责把「content URI」后边的「id」解析出来，
 *          比如「现在这个content URI」是「content://com.ex09_2_contentprovider.diarycontentprovider/diaries/100」，那么这个函数的「返回值」就是「100」。

原文链接：https://blog.csdn.net/dq1005/article/details/47684071
 */
object SDCardUtils {

    const val TAG = "SDCardUtils"

    private fun getContext(): Context {
        return BaseApp.instance
    }

    /* 验证存储空间的可用性 -- 可移除卷（例如 SD卡）在文件系统中属于「外部存储空间」 */
    /**
     * 可移除卷（例如 SD卡） -- 是否「可·读写」
     * [Environment.DIRECTORY_ALARMS]
     */
    fun isExternalStorageWritable(): Boolean {
        return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
    }

    /**
     * 可移除卷（例如 SD卡） -- 是否「可·读」
     */
    fun isExternalStorageReadable(): Boolean {
        return Environment.getExternalStorageState() in setOf(Environment.MEDIA_MOUNTED, Environment.MEDIA_MOUNTED_READ_ONLY)
    }

    /**
     * 获取：「外部存储 -- 本应用·私有目录」
     *
     * 分配「内部存储·分区」作为「外部存储空间」的设备也会提供「SD卡插槽」。
     * 这意味着设备具有「多个」可能包含「外部存储空间」的物理卷，因此您需要选择用于「应用·专属存储空间」的物理卷。
     *
     * 如需访问其他位置，请调用 ContextCompat.getExternalFilesDirs()。
     * 如代码段中所示，返回「数组中的第一个元素」被视为「主外部存储卷」。除非该卷已满或不可用，否则请使用该卷。
     *
     * 注意：如果应用在搭载 Android 4.3（API 级别 18）或更低版本的设备上使用，则该数组仅包含一个表示「主外部存储卷的元素」。
     */
    fun getPrimaryExternalStorage(): File {
        val externalStorageVolumes: Array<out File> = ContextCompat.getExternalFilesDirs(getContext(), null)
        return externalStorageVolumes[0]
    }

    /**
     * 获取：「外部存储 -- 本应用·私有目录」
     * [type]       文件类型
     *  [Environment.DIRECTORY_MUSIC]
     *  [Environment.DIRECTORY_PODCASTS]
     *  [Environment.DIRECTORY_ALARMS]
     *  [Environment.DIRECTORY_RINGTONES]
     *  [Environment.DIRECTORY_NOTIFICATIONS]
     *  [Environment.DIRECTORY_PICTURES]
     *  [Environment.DIRECTORY_DOWNLOADS]
     *  [Environment.DIRECTORY_DCIM]
     *  [Environment.DIRECTORY_DOCUMENTS]
     *  [Environment.DIRECTORY_AUDIOBOOKS]
     *
     *  [null] 传递到 [getExternalFilesDir(String)]。这将返回「外部存储空间」中的「应用·专属根目录」。
     *
     *  返回： /storage/emulated/0/Android/data/包名/files/共享目录
     *  如：[Environment.DIRECTORY_PICTURES]图片，此时返回 /storage/emulated/0/Android/data/包名/files/Pictures
     */
    fun getESPrivateDir(type: String? = null): File {
        val file = getContext().getExternalFilesDir(type)
            ?: throw Throwable("文件不存在：Environment.DIRECTORY_$type")

        if (!file.mkdirs()) Log.e(TAG, "Directory not created") // 如果文件夹·不存在，就创建
        return file
    }

    /**
     * 获取：「外部存储 -- 本应用·私有目录·持久文件」
     * [type]       文件类型
     * [filename]   文件名
     *
     * 注意：为确保应用的性能，请勿多次打开和关闭同一文件。
     */
    fun getESPrivateFile(filename: String, type: String? = null): File{
        return File(getESPrivateDir(type), filename)
    }

    /**
     * 获取：「外部存储 -- 本应用·私有目录·缓存文件」
     * [filename]   文件名
     *
     * 返回： /storage/emulated/0/Android/data/包名/cache
     */
    fun getESPrivateCacheFile(filename: String): File{
        return File(getContext().externalCacheDir, filename)
    }

    /**
     * 删除：「外部存储 -- 本应用·私有目录·缓存文件」
     * [filename]   文件名
     */
    fun deleteESPrivateCacheFile(filename: String): Boolean{
        return getESPrivateCacheFile(filename).delete()
    }


    //------------------------------------------------------------------------
    //                              存储空间大小
    //------------------------------------------------------------------------
    /**
     *
     * 获取 -- 剩余空间
     *
     * 目前这里 测量的是 「主外部存储目录」
     */
    fun getFreeSpaceBytes(): Long {
        return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
            val state = StatFs(getPrimaryExternalStorage().absolutePath)
            val blockSize = state.blockSizeLong                  // 获得「一个扇区·大小」
            val blockCount = state.blockCountLong                // 获得「扇区·总数」
            val availableCount = state.availableBlocksLong       // 获得「可用的扇区·数量」
            val totalSpace = blockCount * blockSize / 1024       // 存储空间大小 in KB
            val freeSpace = availableCount * blockSize / 1024    // 可用的存储空间大小 in KB
            freeSpace

        }else{
            val storageManager = getContext().getSystemService<StorageManager>()!!
            val appSpecificInternalDirUuid: UUID = storageManager.getUuidForPath(getPrimaryExternalStorage())
            val availableBytes: Long = storageManager.getAllocatableBytes(appSpecificInternalDirUuid)
            availableBytes / 1024

//            if (availableBytes >= NUM_BYTES_NEEDED_FOR_MY_APP) {
//                storageManager.allocateBytes(appSpecificInternalDirUuid, NUM_BYTES_NEEDED_FOR_MY_APP)
//            } else {
//                val storageIntent = Intent().apply { action = ACTION_MANAGE_STORAGE }
//                // Display prompt to user, requesting that they choose files to remove.
//            }
        }
    }


    //------------------------------------------------------------------------
    //                              读写文件
    //------------------------------------------------------------------------

    /*
        实现了Closeable接口的对象可调用use函数
        use函数会自动关闭调用者（无论中间是否出现异常）
        Kotlin的File对象和IO流操作变得行云流水
        use函数内部实现也是通过try-catch-finally块捕捉的方式，所以不用担心会有异常抛出导致程序退出
        close操作在finally里面执行，所以无论是正常结束还是出现异常，都能正确关闭调用者
     */
    fun openInputStream(uri: Uri): InputStream?{
        return getContext().contentResolver.openInputStream(uri)
    }

    fun openFileDescriptorInputStream(uri: Uri): InputStream?{
        getContext().contentResolver.openFileDescriptor(uri, "r")?.use {
            return FileInputStream(it.fileDescriptor)
        } ?: return null
    }

    fun openOutputStream(uri: Uri): OutputStream?{
        return getContext().contentResolver.openOutputStream(uri)
    }
    fun openFileDescriptorOutputStream(uri: Uri): OutputStream?{
        getContext().contentResolver.openFileDescriptor(uri, "w")?.use {
            return FileOutputStream(it.fileDescriptor)
        } ?: return null
    }

    //------------------------------------------------------------------------
    //                              MediaStore
    //------------------------------------------------------------------------
    /**
     * 将「媒体项」添加到「现有集合」
     *
     */
    @RequiresApi(Build.VERSION_CODES.Q)
    fun insert() {
        val uri = MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
        val newFileDetails = ContentValues().apply {
            put(MediaStore.Audio.Media.DISPLAY_NAME, "NewFile.mp3")         // 显示名字
        }

        val myFavoriteSongUri = getContext().contentResolver.insert(uri, newFileDetails)
    }

    /**
     * 切换媒体文件的待处理状态
     *
     * 如果您的应用执行可能非常耗时的操作（例如写入媒体文件），那么在处理文件时对其进行独占访问非常有用。
     * 在搭载「Android 10 或更高版本」的设备上，您的应用可以通过将[IS_PENDING]标记的值设为「1」来获取此「独占访问权限」。
     * 如此一来，只有您的应用可以查看该文件，直到您的应用将[IS_PENDING]的值改回「0」。
     *
     */
    @RequiresApi(Build.VERSION_CODES.Q)
    fun waitInsert() {
        val resolver = getContext().contentResolver
        val uri = MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)

        val songDetails = ContentValues().apply {
            put(MediaStore.Audio.Media.DISPLAY_NAME, "MyAudio.mp3")
            put(MediaStore.Audio.Media.IS_PENDING, 1)
        }

        val songContentUri = resolver.insert(uri, songDetails) ?: return

        openFileDescriptorOutputStream(songContentUri).use { outputStream ->
            // Write data into the pending audio file.
        }

        songDetails.clear()
        songDetails.put(MediaStore.Audio.Media.IS_PENDING, 0)
        resolver.update(songContentUri, songDetails, null, null)
    }

    /**
     * 更新应用拥有的「媒体文件」
     *
     * 如果「分区存储」·「不可用或未启用」，下面代码段中所示的过程也适用于「不属于您的应用的文件」
     */
    fun update(uri: Uri) {
        // Updates an existing media item.
        val mediaId = MediaStore.Audio.Media._ID// MediaStore.Audio.Media._ID of item to update.
        val resolver = getContext().contentResolver

        val selection = "${MediaStore.Audio.Media._ID} = ?"
        val selectionArgs = arrayOf(mediaId.toString())

        val updatedSongDetails = ContentValues().apply {
            put(MediaStore.Audio.Media.DISPLAY_NAME, "MyAudio.mp3")
        }

        val numSongsUpdated = resolver.update(uri, updatedSongDetails, selection, selectionArgs)
    }

    /**
     * 更新应用拥有的「媒体文件」
     *
     * 如果「分区存储」·「不可用或未启用」，下面代码段中所示的过程也适用于「不属于您的应用的文件」
     */
    fun update2(uri: Uri) {
//        val resolver = getContext().contentResolver
//        try {
//            resolver.openFileDescriptor(uri, "w")?.use {
//                it.setGrayscaleFilter(it)
//            }
//        } catch (securityException: SecurityException) {
//            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
//                val recoverableSecurityException = securityException as? RecoverableSecurityException ?:
//                throw RuntimeException(securityException.message, securityException)
//
//                recoverableSecurityException.userAction.actionIntent.intentSender?.let {
//                    startIntentSenderForResult(intentSender, image-request-code,
//                        null, 0, 0, 0, null)
//                }
//
//            } else {
//                throw RuntimeException(securityException.message, securityException)
//            }
//        }
    }

    //------------------------------------------------------------------------
    //                              照片中的位置信息
    //------------------------------------------------------------------------
    /**
     * 照片中的位置信息
     *
     */
    fun getImageLocation() {
//        var photoUri: Uri = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
//            cursor.getString(idColumnIndex))
//
//        val latLong = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
//            // When running Android Q, get location data from `ExifInterface`.
//            photoUri = MediaStore.setRequireOriginal(photoUri)
//            openInputStream(photoUri)?.use { stream ->
//                ExifInterface(stream).run {
//                    val latLong: FloatArray = floatArrayOf(0f, 0f)
//                    getLatLong(latLong)
//                    latLong
//                }
//            }
//        } else {
//            // On devices running Android 9 (API level 28) and lower, use the
//            // media store columns.
//            doubleArrayOf(
//                cursor.getFloat(latitudeColumnIndex).toDouble(),
//                cursor.getFloat(longitudeColumnIndex).toDouble()
//            )
//        }
    }


    //------------------------------------------------------------------------
    //                              测试
    //------------------------------------------------------------------------

    /**
     * 加载给定「媒体文件」的缩略图  需要api 29
     */
    @RequiresApi(Build.VERSION_CODES.Q)
    fun loadThumbnail(uri: Uri): Bitmap{
        return getContext().contentResolver.loadThumbnail(uri, Size(640, 480), null)
    }

    /**
     * 测试使用  需要api 29
     *
     * 需要权限： [android.permission.READ_EXTERNAL_STORAGE]
     *
     */
    @RequiresApi(Build.VERSION_CODES.Q)
    fun getVideoFileInfo(){
        // Container for information about each video.
        data class Video(val uri: Uri, val name: String, val duration: Int, val size: Int)

        val videoList = mutableListOf<Video>()

        val projection = arrayOf(
            MediaStore.Video.Media._ID,
            MediaStore.Video.Media.DISPLAY_NAME,
            MediaStore.Video.Media.DURATION,
            MediaStore.Video.Media.SIZE
        )

        val selection = "${MediaStore.Video.Media.DURATION} >= ?"
        val selectionArgs = arrayOf(TimeUnit.MILLISECONDS.convert(5, TimeUnit.MINUTES).toString()) // 5分钟
        val sortOrder = "${MediaStore.Video.Media.DISPLAY_NAME} ASC"

        val query = getContext().contentResolver.query(
            MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
            projection,
            selection,
            selectionArgs,
            sortOrder
        )

        query?.use { cursor ->
            // Cache column indices.
            val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID)
            val nameColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME)
            val durationColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION)
            val sizeColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE)

            while (cursor.moveToNext()) {
                // Get values of columns for a given video.
                val id = cursor.getLong(idColumn)
                val name = cursor.getString(nameColumn)
                val duration = cursor.getInt(durationColumn)
                val size = cursor.getInt(sizeColumn)

                // 把id和contentUri连接成一个新的Uri
                val contentUri: Uri = ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id)

                videoList += Video(contentUri, name, duration, size)
            }
        }
    }

}